/**
 * Copyright 2015 jianglibo@gmail.com
 *
 */
package com.m3958.logcollector.whl;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author jianglibo@gmail.com
 *         2016年1月10日
 *
 */
public class FileNameUtils {

    private String prefix = "i";

    private int numLimit = 10;

    private Path workingDir;

    private long lastFileNumber;

    private FilenameFilter ff = new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
            return !name.endsWith(".done");
        }
    };

    public FileNameUtils(Path workingDir, int numLimit) throws IOException {
        this.workingDir = workingDir;
        this.numLimit = numLimit;
        if (!Files.exists(workingDir)) {
            Files.createDirectory(workingDir);
        }
        setLastFileNumber(getLastFileNumberFromDisk());
    }

    /**
     * if oldWrite exists, increament lastFileNumber.
     * 
     * @param oldWriter
     * @return
     * @throws IOException
     */
    public PrintWriter switchWriter(Writer oldWriter) throws IOException {
        if (oldWriter != null) {
            oldWriter.close();
            Files.createFile(getLastFilePathFromLastFileNumber().getParent().resolve(getLastFilePathFromLastFileNumber().getFileName().toString() + ".done"));
            this.lastFileNumber++;
        }

        BufferedWriter bw = Files.newBufferedWriter(getLastFilePathFromLastFileNumber(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
        return new PrintWriter(bw, true);
    }

    public List<Path> findUndonePathes() {
        Path folder = getWorkingDir();
        return findUndones().stream().map(fn -> folder.resolve(fn)).collect(Collectors.toList());
    }

    public List<String> findUndones() {
        return findUndoneOrDones(getWorkingDir(), false);
    }

    public List<Path> findDonePathes() {
        Path folder = getWorkingDir();
        return findDones().stream().map(fn -> folder.resolve(fn)).collect(Collectors.toList());
    }

    public List<String> findDones() {
        return findUndoneOrDones(getWorkingDir(), true);
    }

    public Path getLastFilePathFromLastFileNumber() {
        return getWorkingDir().resolve(getFileNameFromFileNumber());
    }

    /**
     * this is not an atomic action.should take care.
     */
    public void removeDones() {
        findDonePathes().forEach(p -> {
            try {
                Files.delete(p);
            } catch (Exception e) {
            }
        });
    }

    public List<String> findUndoneOrDones(Path folder, boolean done) {
        List<String> filenames = new ArrayList<>();
        for (String f : listSortedFileName()) {
            boolean fileExist = Files.exists(folder.resolve(f));
            boolean doneExist = Files.exists(folder.resolve(f + ".done"));

            boolean toAdd;
            if (done) {
                toAdd = fileExist && doneExist;
            } else {
                toAdd = fileExist && !doneExist;
            }
            if (toAdd) {
                filenames.add(f);
            }
        }
        return filenames;
    }

    public String[] listSortedFileName() {
        return listSortedFileName(getWorkingDir());
    }

    public String[] listSortedFileName(Path folder) {
        String[] ss = folder.toFile().list(ff);
        Arrays.sort(ss);
        return ss;
    }

    public String getFileNameFromFileNumber() {
        return getFileNameFromFileNumber(getLastFileNumber());
    }

    public String getFileNameFromFileNumber(long num) {
        String snum = String.valueOf(num);
        if (snum.length() < numLimit) {
            snum = zeros(numLimit - snum.length()) + snum;
        }
        return prefix + snum;
    }

    public long getLastFileNumberFromDisk() {
        return getLastFileNumberFromDisk(getWorkingDir());
    }

    public long getLastFileNumberFromDisk(Path folder) {
        String[] ss = folder.toFile().list(ff);
        if (ss.length > 0) {
            String lns = ss[ss.length - 1];
            long ln = filenameToLong(lns);
            if (Files.exists(getWorkingDir().resolve(lns + ".done"))) {
                ln++;
            }
            return ln;
        }
        return 0L;
    }

    private String zeros(int num) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < num; i++) {
            sb.append("0");
        }
        return sb.toString();
    }

    public long filenameToLong(String fn) {
        String snum = fn.substring(prefix.length());
        return Long.valueOf(snum);
    }

    public Path getWorkingDir() {
        return workingDir;
    }

    public void setWorkingDir(Path workingDir) {
        this.workingDir = workingDir;
    }

    public long getLastFileNumber() {
        return lastFileNumber;
    }

    public void setLastFileNumber(long lastFileNumber) {
        this.lastFileNumber = lastFileNumber;
    }
}
